package com.xs.aifitnessagent.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xs.aifitnessagent.agent.FitnessManus;
import com.xs.aifitnessagent.app.FitnessApp;
import com.xs.aifitnessagent.chatmemory.RedisChatMemory;
import com.xs.aifitnessagent.entities.History;
import com.xs.aifitnessagent.entities.dto.ChatDTO;
import com.xs.aifitnessagent.entities.vo.MessageVo;
import com.xs.aifitnessagent.entities.vo.MessageDetailVo;
import com.xs.aifitnessagent.entities.vo.UserVO;
import com.xs.aifitnessagent.enums.ReturnCodeEnum;
import com.xs.aifitnessagent.exp.BusinessException;
import com.xs.aifitnessagent.mapper.HistoryMapper;
import com.xs.aifitnessagent.resp.ResultData;
import com.xs.aifitnessagent.service.AiService;
import com.xs.aifitnessagent.service.MessageService;
import com.xs.aifitnessagent.util.IpUtil;
import com.xs.aifitnessagent.util.ThreadLocalUtil;
import io.micrometer.common.util.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author lambor
 *
 */
@RequestMapping("/ai")
@RestController
@Slf4j
public class FitnessController {

    @Resource
    private RedisTemplate<String, String> stringRedisTemplate;

    @Resource
    private FitnessApp fitnessApp;

    @Resource
    private AiService aiService;

    @Resource
    private HistoryMapper historyMapper;

    @Resource
    private RedisChatMemory redisChatMemory;

    @Resource
    private MessageService messageService;

    @Resource
    private ToolCallback[] allTools;

    @Resource
    private ChatModel dashscopeChatModel;

    @GetMapping("/chat/sync")
    @Operation(summary = "同步调用 AI 健身大师应用")
    public String doChatWithAppSync(String message, String chatId) {
        return fitnessApp.doChat(message, chatId);
    }

    @GetMapping(value = "/chat/sse/emitter", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @Operation(summary = "SSE 流式调用 AI 健身大师应用")
    public SseEmitter doChatWithAppSseEmitter(ChatDTO chatDTO, HttpServletRequest request) throws IOException {
        String uid = stringRedisTemplate.opsForValue().get("userId:" + chatDTO.getUid().toString());
        String chatId = chatDTO.getChatId();
        String message = chatDTO.getMessage();

        if (StringUtils.isEmpty(uid)) {
            throw new BusinessException(ReturnCodeEnum.RC403.getCode(), ReturnCodeEnum.RC403.getMessage());
        }

        // 验证chatId不能为空或无效，避免Spring AI使用默认值导致数据混淆
        if (StringUtils.isEmpty(chatId) || "null".equals(chatId) || "undefined".equals(chatId)) {
            throw new BusinessException("400", "chatId不能为空或无效");
        }

        // 将用户 ID 设置到 ThreadLocal 中，供后续操作使用
        ThreadLocalUtil.setUserId(Integer.parseInt(uid));

        // 使用分布式锁存入 chatId 用于用户首次会话产生会话标题
        Boolean titleFlag = stringRedisTemplate.opsForValue().setIfAbsent("userId:" + uid + ":message", message);
        createTitle(chatDTO, titleFlag, chatId, uid);

        // 创建一个超时时间较长的 SseEmitter —— 3 分钟
        SseEmitter sseEmitter = new SseEmitter(180000L);
        // 续期
        stringRedisTemplate.expire("userId:" + uid, 60, TimeUnit.MINUTES);

        // todo 模型使用限制
        /*  暂时关闭 IP 访问次数限制，便于测试
        String ipAddress = "ip:" + IpUtil.getIpAddress(request);
        String count = stringRedisTemplate.opsForValue().get(ipAddress);
        if (!StringUtils.isEmpty(count) && Long.parseLong(count) > 20) {
            sseEmitter.send("您已超过请求最大次数限制，请明日再来");
            return sseEmitter;
        } else {
            stringRedisTemplate.opsForValue().increment(ipAddress);
        }*/
        
        // 获取 Flux 响应式数据流并且直接通过订阅推送给 SseEmitter
        fitnessApp.doChatWithStream(chatDTO)
                .subscribe(chunk -> {
                            try {
                                sseEmitter.send(chunk);
                            } catch (IOException e) {
                                sseEmitter.completeWithError(e);
                            }
                        },
                        sseEmitter::completeWithError, 
                        () -> {
                            // 流完成
                            sseEmitter.complete();
                        }
                );
        return sseEmitter;

    }

    @GetMapping(value = "/chat/manus", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @Operation(summary = "流式调用 Manus 智能体")
    public SseEmitter doChatWithManus(String message) {
        log.info("收到SSE请求: {}", message);
        FitnessManus fitnessManus = new FitnessManus(allTools, dashscopeChatModel);
        return fitnessManus.runStream2(message);
    }

    @PostMapping("/create")
    @Operation(summary = "新建会话")
    public UserVO create() {
        UserVO userVO = new UserVO();
        long uid = StpUtil.getLoginIdAsLong();
        // 开启新会话后删除上一次用户的 chatId 缓存
        stringRedisTemplate.delete("userId:" + uid + ":message");

        String chatId = UUID.randomUUID().toString();
        userVO.setChatId(chatId);
        userVO.setId(uid);
        stringRedisTemplate.opsForValue().set("userId:"+ uid, String.valueOf(uid),60, TimeUnit.MINUTES);
        return userVO;
    }

    @PostMapping("/getChatMemoryList")
    @Operation(summary = "获取会话历史")
    public ResultData<List<History>> getHistoryList(@RequestBody ChatDTO chatDTO) {
        Long uid = chatDTO.getUid();
        Integer status = chatDTO.getStatus();
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }

        List<History> histories = historyMapper.selectList(new LambdaQueryWrapper<History>()
                .eq(History::getUserId, uid)
                .eq(History::getStatus, status)
                .orderByDesc(History::getCreateTime)
                .last("limit 10"));
        return ResultData.success(histories);
    }

    @PostMapping("/getChatMemory")
    @Operation(summary = "获取会话记忆")
    public ResultData<MessageVo> getChatMemory(@RequestBody ChatDTO chatDTO){
        Long uid = chatDTO.getUid();
        String chatId = chatDTO.getChatId();
        if (chatId == null) {
            throw new BusinessException("500", "chatId不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }

        History history = historyMapper.selectOne(new LambdaQueryWrapper<History>().eq(History::getChatId, chatId).eq(History::getUserId, uid));
        if (ObjectUtil.isEmpty(history)){
            throw new BusinessException("500", "用户没有此会话");
        }
        
        // 优先从数据库获取消息
        List<com.xs.aifitnessagent.entities.Message> dbMessages = messageService.getMessagesByChatId(chatId, uid.toString());
        
        List<Message> messages;
        List<MessageDetailVo> detailMessages = null;
        
        if (!dbMessages.isEmpty()) {
            // 从数据库获取到消息，转换为Spring AI的Message格式
            messages = dbMessages.stream()
                    .map(dbMsg -> {
                        if ("USER".equals(dbMsg.getMessageType())) {
                            return new org.springframework.ai.chat.messages.UserMessage(dbMsg.getContent());
                        } else {
                            return new org.springframework.ai.chat.messages.AssistantMessage(dbMsg.getContent());
                        }
                    })
                    .map(Message.class::cast)
                    .toList();
            
            // 同时构建详细消息信息
            detailMessages = dbMessages.stream()
                    .map(dbMsg -> {
                        MessageDetailVo detail = new MessageDetailVo();
                        detail.setId(dbMsg.getId());
                        detail.setMessageType(dbMsg.getMessageType());
                        detail.setContent(dbMsg.getContent());
                        detail.setIsEdited(dbMsg.getIsEdited());
                        detail.setOriginalContent(dbMsg.getOriginalContent());
                        detail.setCreateTime(dbMsg.getCreateTime());
                        detail.setModelName(dbMsg.getModelName());
                        detail.setTokenCount(dbMsg.getTokenCount());
                        return detail;
                    })
                    .toList();
            
            log.info("从数据库获取消息: chatId={}, count={}", chatId, messages.size());
        } else {
            // 数据库中没有消息，尝试从Redis获取（兼容旧数据）
            messages = redisChatMemory.get(chatId);
            log.info("从Redis获取消息: chatId={}, count={}", chatId, messages.size());
        }
        
        return ResultData.success(new MessageVo(history.getTitle(), messages, detailMessages));
    }

    @PostMapping("/deleteChatMemory")
    @Operation(summary = "删除会话记忆")
    public ResultData deleteChatMemory(@RequestBody ChatDTO chatDTO){
        log.info("deleteChatMemory:{}",chatDTO);
        Long uid = chatDTO.getUid();
        String chatId = chatDTO.getChatId();
        if (StringUtils.isEmpty(chatId)) {
            throw new BusinessException("500", "chatId不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }
        int delete = historyMapper.delete(new LambdaQueryWrapper<History>().eq(History::getChatId, chatId).eq(History::getUserId, uid));
        if (delete > 0) {
            // 删除Redis中的消息
            redisChatMemory.clear(chatId);
            // 删除数据库中的消息
            messageService.deleteMessagesByChatId(chatId, uid.toString());
        }
        log.info("delete:{}", delete==1? "删除：" + chatId + "成功" : "删除：" + chatId + "失败");

        return ResultData.success(delete == 1? "删除成功" : "删除失败");
    }

    @PostMapping("/updateChatMemoryTitle")
    @Operation(summary = "更新会话记忆标题")
    public ResultData updateChatMemoryTitle(@RequestBody ChatDTO chatDTO){
        log.info("updateChatMemoryTitle:{}",chatDTO);
        Long uid = chatDTO.getUid();
        String chatId = chatDTO.getChatId();
        String title = chatDTO.getTitle();
        if (org.apache.commons.lang3.StringUtils.isEmpty(title)) {
            throw new BusinessException("500", "chatId不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }
        int update = historyMapper.update(new LambdaUpdateWrapper<History>().eq(History::getChatId, chatId).eq(History::getUserId, uid).set(History::getTitle, title).set(History::getCreateTime, new Date()));
        log.info("update:{}", update == 1? "修改：" + chatId + "成功" : "修改：" + chatId + "失败");
        return ResultData.success(update == 1? "修改成功" : "修改失败");
    }

    @PostMapping("/editMessage")
    @Operation(summary = "编辑用户消息")
    public ResultData<com.xs.aifitnessagent.entities.Message> editMessage(@RequestBody ChatDTO chatDTO) {
        Long messageId = chatDTO.getMessageId();
        String newContent = chatDTO.getMessage();
        Long uid = chatDTO.getUid();

        if (messageId == null) {
            throw new BusinessException("500", "消息ID不能为空");
        }
        if (org.apache.commons.lang3.StringUtils.isEmpty(newContent)) {
            throw new BusinessException("500", "消息内容不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }

        try {
            com.xs.aifitnessagent.entities.Message updatedMessage = messageService.editUserMessage(messageId, newContent, uid.toString());
            log.info("编辑消息成功：messageId={}, userId={}", messageId, uid);
            return ResultData.success(updatedMessage);
        } catch (RuntimeException e) {
            log.error("编辑消息失败：messageId={}, userId={}, error={}", messageId, uid, e.getMessage());
            throw new BusinessException("500", e.getMessage());
        }
    }

    @PostMapping("/getMessageDetail")
    @Operation(summary = "获取消息详情")
    public ResultData<com.xs.aifitnessagent.entities.Message> getMessageDetail(@RequestBody ChatDTO chatDTO) {
        Long messageId = chatDTO.getMessageId();
        Long uid = chatDTO.getUid();

        if (messageId == null) {
            throw new BusinessException("500", "消息ID不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }

        com.xs.aifitnessagent.entities.Message message = messageService.getById(messageId);
        if (message == null) {
            throw new BusinessException("500", "消息不存在");
        }

        // 验证用户权限
        if (!message.getUserId().equals(uid.toString())) {
            throw new BusinessException("403", "无权限查看此消息");
        }

        return ResultData.success(message);
    }

    @PostMapping("/deleteMessagesAfter")
    @Operation(summary = "删除指定消息之后的所有消息")
    public ResultData<Integer> deleteMessagesAfter(@RequestBody ChatDTO chatDTO) {
        Long messageId = chatDTO.getMessageId();
        String chatId = chatDTO.getChatId();
        Long uid = chatDTO.getUid();

        if (messageId == null) {
            throw new BusinessException("500", "消息ID不能为空");
        }
        if (org.apache.commons.lang3.StringUtils.isEmpty(chatId)) {
            throw new BusinessException("500", "聊天ID不能为空");
        }
        if (uid == null) {
            throw new BusinessException("500", "用户ID不能为空");
        }

        try {
            int deletedCount = messageService.deleteMessagesAfter(messageId, chatId, uid.toString());
            log.info("删除后续消息成功：messageId={}, chatId={}, userId={}, deletedCount={}",
                    messageId, chatId, uid, deletedCount);
            return ResultData.success(deletedCount);
        } catch (RuntimeException e) {
            log.error("删除后续消息失败：messageId={}, chatId={}, userId={}, error={}",
                    messageId, chatId, uid, e.getMessage());
            throw new BusinessException("500", e.getMessage());
        }
    }

    /**
     * 异步产生标题
     *
     */
    @Async
    public void createTitle(ChatDTO chatDTO, Boolean titleFlag, String chatId, String uid) {
        if (Boolean.TRUE.equals(titleFlag)) {
            String title = aiService.doChatGetTitle(chatDTO);
            History historyFlag = historyMapper.selectOne(new LambdaQueryWrapper<History>().eq(History::getChatId, chatId));
            if (ObjectUtil.isEmpty(historyFlag)) {
                History history = new History();
                history.setTitle(title);
                history.setUserId(uid);
                history.setChatId(chatId);
                history.setStatus(1);
                historyMapper.insert(history);
            }
        }
    }

}
